/**
 * 
 */
package com.moth.cloudsequence;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.alibaba.fastjson.JSON;
import com.mdfs.lang.MDFSInteger;
import com.moth.ISystemConstant;
import com.moth.MothException;
import com.moth.utils.ConvertHelper;
import com.moth.utils.DateUtil;
import com.moth.utils.FileOperateHelper;
import com.moth.utils.StringHelper;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * 序列线程 作者:龙色波 日期:2013-12-25
 */
public class CloudSequenceThread extends Thread {
	/**
	 * 通知bean
	 */
	private SequenceRequestNotifyBean notifyBean;
	private boolean exist = false;
	private String serverPath;
	// 格式序列化值串
	private final String FMT_SEQ_VALUE = "%value";
	/**
	 * 管理序列个数
	 */
	private MDFSInteger sequenceSize = new MDFSInteger(0);
	private String uuid;
	/**
	 * 文件锁输出流
	 */
    private OutputStream fileLockOutputStream = null;
	public CloudSequenceThread(SequenceRequestNotifyBean notifyBean,
			String serverPath) {
		this.notifyBean = notifyBean;
		this.serverPath = serverPath;
	}

	@Override
	public void run() {
		// 检查并创建服务器目录
		if (!FileOperateHelper.isExists(serverPath)) {
			try {
				FileOperateHelper.newFolder(serverPath);
			} catch (MothException e) {
				e.printStackTrace();
				return;
			}
		}
		while (!isExist()) {
			List<SequenceItem> items;
			synchronized (notifyBean) {
				try {
					notifyBean.wait(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				items = notifyBean.removeAll();
			}

			if (items != null && items.size() > 0) {
				
				System.out.println("--"+DateUtil.getNowTimeStampString()+",thread id:"+this.getId()+" items size:" + items.size());
				List<SequenceItem> groupItem = new ArrayList<SequenceItem>();
				while (!items.isEmpty()) {
					SequenceItem item;
					groupItem.clear();
					item = items.remove(0);
					groupItem.add(item);
					// System.out.println("make group item...");
					for (int i = items.size() - 1; i >= 0; i--) {
						if (items.get(i).getSequenceFileName().equals(
								item.getSequenceFileName())) {
							groupItem.add(items.remove(i));
						}
					}
					 System.out.println("groupItem size:"+groupItem.size());
					
					// System.out.println("for(SequenceItem item1:groupItem)");
					//成批获取序列值
					List<String> seqVals = null;
					try {
						seqVals = getNextSeqValFromCloud(item,groupItem.size());
					} catch (Exception e1) {
						e1.printStackTrace();
						return;
					}
					if(seqVals!=null){
						for (SequenceItem item1 : groupItem) {						
							// 填写序列值文件
							try {
								FileOperateHelper.writeStringToFile(new File(item1
										.getSequenceValFileName()), seqVals.remove(0)
										+ "\r\n", ISystemConstant.UTF_8_ENCODE);
							} catch (Exception e) {
								e.printStackTrace();
								continue;
							}
							// 通知外面线程
							// System.out.println("synchronized (item1:"+item1.getSequenceValFileName()+")");
							synchronized (item1) {
								item1.setFinish(true);
								// System.out.println("item1.notify(item1:"+item1.getSequenceValFileName()+")");
								item1.notify();
							}
	
						}
				  }
				}
			}
		}
	}

	/**
	 * 获取下一批序列值
	 * @param size 序列值数组长度  
	 * 
	 * @param sequenceName
	 * @return
	 * @throws Exception 
	 */
	private List<String> getNextSeqValFromCloud(SequenceItem item, int size)
			throws Exception {
		String sequenceName = item.getSequenName();
		//记录开始值
		Long startCurrentValue =0L;
		// 创建序列子目录
		String subPath;
		if (serverPath.endsWith(File.separator)) {
			subPath = serverPath + sequenceName + File.separator;
		} else {
			subPath = serverPath + File.separator + sequenceName + File.separator;
		}
		if (!FileOperateHelper.isExists(subPath)) {
			try {
				FileOperateHelper.newFolder(subPath);
			} catch (MothException e) {
				e.printStackTrace();
				// 如果不是并发创建失败
				if (!FileOperateHelper.isExists(subPath)) {
					throw e;
				}
			}
		}
		// 构造锁文件
		String lockFileName;
		lockFileName = subPath + ISystemConstant.SEQUENCE_LOCK_FILE;
		// 获取UUID
		uuid = getUUID();
		long waittime = ISystemConstant.THREAD_WAIT_SLEEP_TIME;
		String cmpuuid;
		File lockFile = new File(lockFileName);
		while (true) {
			try {
				if (FileOperateHelper.isExists(lockFileName)) {
					waittime = WaitForatime(waittime);
					continue;
				} else {
					fileLockOutputStream =newFile(lockFileName, uuid);
					if(fileLockOutputStream==null){
						waittime = WaitForatime(waittime);
					}else{
						// 重新读入比较,如果相同则认为获取锁成功，否则继续等待
						cmpuuid = FileOperateHelper.readFileToString(lockFile,
								ISystemConstant.UTF_8_ENCODE);
						cmpuuid = StringHelper.trimCrlf(cmpuuid);
						if (uuid.equals(cmpuuid)) {
							break;
						} else {
							//关闭文件锁输出流
							fileLockOutputStream.close();
							fileLockOutputStream = null;
							waittime = WaitForatime(waittime);
						}
					}
				}
			} catch (MothException e) {
				waittime = WaitForatime(waittime);
			} catch (IOException e) {
				waittime = WaitForatime(waittime);
			}
		}
		// 已经获取到锁
		SequenceFormatBean seqbean = null;
		long end=0;
		List<String> retList = new ArrayList<String>();
		try {
			boolean bexist = false;
			// 检查序列配置文件是否存在
			String sequenceFileName;
			if (serverPath.endsWith(File.separator)) {
				sequenceFileName = serverPath + sequenceName+ ISystemConstant.SEQUENCE_SUFFIX;
			}else{
				sequenceFileName = serverPath + File.separator+ sequenceName+ ISystemConstant.SEQUENCE_SUFFIX;
			}
			bexist = FileOperateHelper.isExists(sequenceFileName);
			if (!bexist) {
				throw new MothException("序列文件:"+sequenceFileName+"不存在!");
			} else {
				waittime = ISystemConstant.THREAD_WAIT_SLEEP_TIME;
				
				while (true) {
					try {
						seqbean = getSeqBeanFromFile(sequenceFileName);
						break;
					} catch (MothException e) {
						e.printStackTrace();
						if (waittime > 20000) {
							waittime = ISystemConstant.THREAD_WAIT_SLEEP_TIME;
						} else {
							waittime = waittime + 1;
						}
						try {
							Thread.sleep(waittime);
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						}
					} catch (IOException e) {
						e.printStackTrace();
						break;
					}
				}
				//当前值+1作为开始值
				//startCurrentValue = seqbean.getCurrentValue()+1;
				// 序列值加n
				seqbean = updateSequenceBean(seqbean,item.getParams(),size);
				end = seqbean.getCurrentValue();
			}
			//更新序列文件
			saveSeqBeanToFile(sequenceFileName, seqbean);	
			//上面先解锁，后面慢慢格式化
			startCurrentValue = end - size+1;			
			System.out.println("Thread id:"+this.getId()+",start:"+startCurrentValue+",end:"+end);
			for(long seqVal=startCurrentValue;seqVal<=end;seqVal++){
				retList.add(formatSeqVal(seqbean, item.getParams(), seqVal));
			}
		} finally {
			//先关闭文件输出流
			if(fileLockOutputStream!=null){
				fileLockOutputStream.close();
			}
			// 删除锁文件
			waittime = ISystemConstant.THREAD_WAIT_SLEEP_TIME;
			while (FileOperateHelper.isExists(lockFileName)) {
				try {
					FileOperateHelper.delFile(lockFileName);
					break;
				} catch (MothException e) {
					System.out.println("Retry delFile,uuid:" + uuid);
					waittime = WaitForatime(waittime);
				}
			}
		}		
		return retList;
	}
	/**
	 * 等待一段时间
	 * @param waittime
	 * @return
	 */
	private long WaitForatime(long waittime) {
		try {
			if (waittime > 2000) {
				waittime = ISystemConstant.THREAD_WAIT_SLEEP_TIME;
			} else {
				waittime = waittime + 1;
			}
			Thread.sleep(waittime);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		return waittime;
	}
	/**
	 * 序列值加n
	 * @param seqbean 
	 * @param params 参数数组
	 * @param size 
	 * @return
	 * @throws MothException 
	 */
	private SequenceFormatBean updateSequenceBean(SequenceFormatBean seqbean,Object[] params, long size ) throws MothException {
		Timestamp sqlDate;
		sqlDate = (Timestamp) seqbean.getUpdateDate();
		if(sqlDate==null){
			sqlDate = new Timestamp(System.currentTimeMillis());
		}
		// 取出周期类型
		String cycleDateType = ISystemConstant.CYCLE_TYPE_NO;
		if (seqbean.getCycleType() != null) {
			cycleDateType = seqbean.getCycleType();
		}
		Long curVal;
		String fmtCurVal;
		Long currentValue;
		// 判断是否有周期
		if (!ISystemConstant.CYCLE_TYPE_NO.equals(cycleDateType)) {
			// 按年
			if (ISystemConstant.CYCLE_TYPE_YEAR.equals(cycleDateType)) {
				Integer curryear = DateUtil.getCurrentYear();
				Integer dbYear = 0;
				try {
					dbYear = ConvertHelper.toInteger(DateUtil.getDateStr(
							sqlDate, "yyyy"));
				} catch (Exception e) {
					throw new MothException(e.getMessage());
				}
				if (!dbYear.equals(curryear)) {
					// 年份不同,需要从size开始
					curVal = size;
				} else {
					currentValue = seqbean.getCurrentValue();
					// 当期值加 size
					if(currentValue!=null){
						curVal = currentValue;
					}else{
						curVal = 0L;
					}
					curVal = curVal + size;
				}
				// 按月
			} else if (ISystemConstant.CYCLE_TYPE_MONTH.equals(cycleDateType)) {
				Integer currMonth = DateUtil.getCurrentMonth();
				Integer dbMonth = 0;
				try {
					dbMonth = ConvertHelper.toInteger(DateUtil.getDateStr(
							sqlDate, "MM"));
				} catch (Exception e) {
					throw new MothException(e.getMessage());
				}
				if (!dbMonth.equals(currMonth)) {
					// 月份不同,需要从size开始
					curVal = size;
				} else {
					currentValue = seqbean.getCurrentValue();
					// 当期值加size
					if(currentValue!=null){
						curVal = currentValue;
					}else{
						curVal = 0L;
					}
					curVal = curVal + size;
				}
				// 按天
			} else if (ISystemConstant.CYCLE_TYPE_DAY.equals(cycleDateType)) {
				Integer currDay = DateUtil.getCurrentDay();
				Integer dbDay = 0;
				try {
					dbDay = ConvertHelper.toInteger(DateUtil.getDateStr(
							sqlDate, "dd"));
				} catch (Exception e) {
					throw new MothException(e.getMessage());
				}
				if (!dbDay.equals(currDay)) {
					// 天不同,需要从size开始
					curVal = size;
				} else {
					currentValue = seqbean.getCurrentValue();
					// 当期值加size
					if(currentValue!=null){
						curVal = currentValue;
					}else{
						curVal = 0L;
					}
					curVal = curVal + size;
				}
				// 其他按无周期处理
			} else {
				currentValue = seqbean.getCurrentValue();
				// 当期值加size
				if(currentValue!=null){
					curVal = currentValue;
				}else{
					curVal = 0L;
				}
				curVal = curVal + size;
			}
		} else {
			// 当期值加size
			currentValue = seqbean.getCurrentValue();
			if(currentValue!=null){
				curVal = currentValue;
			}else{
				curVal = 0L;
			}
			curVal = curVal + size;
		}
		// 格式当期值为字符串
		fmtCurVal = formatSeqVal(seqbean, params, curVal);
		//更新序列bean当前值和格式化值
		seqbean.setCurrentValue(curVal);
		seqbean.setCurrentFmtValue(fmtCurVal);
		//更新日期
		seqbean.setUpdateDate(new Timestamp(System.currentTimeMillis()));
		return seqbean;
	}
	/**
	 * 格式化序列值
	 * @param seqbean 序列bean
	 * @param params 格式化参数
	 * @param curVal 参数值
	 * @return
	 * @throws MothException
	 */
	private String formatSeqVal(SequenceFormatBean seqbean, Object[] params,
			Long curVal) throws MothException {
		String fmtCurVal;
		int sequenceLen;
		sequenceLen = seqbean.getSequenceLen();
	

		String curValFmt = "%0" + ConvertHelper.toString(sequenceLen) + "d";
		String strCurVal = String.format(curValFmt, curVal);
		// 获取序列格式化规则
		String sequenRule = seqbean.getSequenceRule();
		if (StringHelper.isEmpty(sequenRule)) {
			throw new MothException("序列名称:" + seqbean.getSequenceName() + "的规则尚未定义!");
		}
		// 将规则中的%value替换为strCurVal,然后再格式化
		sequenRule = StringHelper.replaceAll(sequenRule, FMT_SEQ_VALUE,
				strCurVal).toString();
		fmtCurVal = String.format(sequenRule, params);
		return fmtCurVal;
	}

	public SequenceRequestNotifyBean getNotifyBean() {
		return notifyBean;
	}

	public int getSequenceSize() {
		synchronized (sequenceSize) {
			return sequenceSize.getValue();
		}

	}

	public boolean isExist() {
		return exist;
	}

	public void setExist(boolean exist) {
		this.exist = exist;
	}

	public void setSequenceSize(int sequenceSize) {
		synchronized (this.sequenceSize) {
			this.sequenceSize.setValue(sequenceSize);
		}

	}

	/**
	 * 获取UUID
	 * 
	 * @return
	 */
	private String getUUID() {
		if (StringHelper.isEmpty(uuid)) {
			uuid = UUID.randomUUID().toString();
		}
		return uuid;
	}
	/**
	 * 将序列文件转换为序列bean
	 * @throws IOException 
	 * @throws IOException 
	 */
	private SequenceFormatBean getSeqBeanFromFile(String sequenceFileName) throws MothException, IOException {
		FileInputStream instream = null;
		try{
			instream = new FileInputStream(sequenceFileName);
			SequenceFormatBean bean;
			int len;
			len = instream.available();
			byte buff[] = new byte[len];
			if(instream.read(buff)!=len){
				throw new MothException("读取文件:"+sequenceFileName+"失败!");
			}
			String json = new String(buff,ISystemConstant.UTF_8_ENCODE);
			bean = JSON.parseObject(json, SequenceFormatBean.class);
			return bean;
		}catch(FileNotFoundException e){
			throw e;
		}catch(Exception e){
			throw new MothException(e);
		}finally{
			if(instream!=null){
				instream.close();
			}
		}
	}
	/**
	 * 将bean保存为xml文件
	 * @param sequenceFileName
	 * @param bean
	 * @throws Exception 
	 */
	private void saveSeqBeanToFile(String sequenceFileName,SequenceFormatBean bean) throws Exception {
		String json = JSON.toJSONString(bean);
		FileOperateHelper.writeStringToFile(new File(sequenceFileName), json, ISystemConstant.UTF_8_ENCODE);
	}
	/**
	 * 新建文件
	 * 
	 * @param filePathAndName
	 *            String 文件路径及名称 如c:/fqf.txt
	 * @param fileContent
	 *            String 文件内容
	 * @return 
	 * @return boolean
	 * @throws IOException
	 * @throws MothException 
	 */
	private  FileOutputStream newFile(String filePathAndName, String fileContent)
			throws IOException, MothException {
		FileOutputStream out = new FileOutputStream(filePathAndName);		
		//如果为空指针,则不写数据到文件
		try{
			if(fileContent!=null){
              out.write(fileContent.getBytes());
			}else{
			  out.write("".getBytes());
			}
			return out;
		}catch(Exception e){
			if(out!=null){
				out.close();
			}
		}
		return null;
	}
}
